<?php

class Database {

	static protected $dbhLink = NULL;
	/**
	 *
	 * @var PDO
	 */
	protected $dbh;

	function dbh() {
		return $this->dbh;
	}

	/**
	 *
	 * @param string $host
	 * @param string $database
	 * @param string $user
	 * @param string $pass
	 */
	function __construct($host = null, $database = null, $user = 'root', $pass = '') {
		if( $host === null ) extract( require(APP_PATH . '/config/database.php') );
		if( self::$dbhLink === NULL ) {
			try {
				$this->dbh = new PDO(
					"mysql:dbname=$dbname;host=$host",
					$username, $password
				);
				$this->dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
			}
			catch( Exception $e ) {
				throw new Exception('Could not connect to database', JF_Exception::COULD_NOT_CONNECT_TO_DATABASE );
			}
			self::$dbhLink = $this->dbh;
		}
		else $this->dbh = self::$dbhLink;
	}

	function executeFile( $sqlFile, array $params = null, array $otherParams =null ) {
		$f = new SQLFile( $this->dbh );
		$result = $f->loadAndPrepare( $sqlFile, $params, $otherParams );
		$result->execute();
		return;
	}

	function fetchFile( $sqlFile, array $params = null ) {
		$f = new SQLFile( $this->dbh );
		$result = $f->loadAndPrepare( $sqlFile, $params );
		$result->execute();
		return $result->fetchAll( PDO::FETCH_ASSOC );
	}
	
	function execute( $sql, array $params = null ) {
		$s = $this->dbh->prepare( $sql ); 
		if( $s === false ) throw new Exception($this->dbh->errorInfo, $this->dbh->errorCode);
		if( $s->execute( $params ) ) return $s->rowCount();
		else throw new Exception($this->dbh->errorInfo, $this->dbh->errorCode);
	}
	
	function getOne( $sql, array $params = null ) {
		$s = $this->dbh->prepare( $sql );
		if( $s->execute( $params ) ) {
			$data = $s->fetch( PDO::FETCH_NUM );
			if( $data !== false ) return $data[0];
			else return false;
		}
		else throw new Exception($this->dbh->errorInfo, $this->dbh->errorCode);
	}
	
	function getRow( $sql, array $params = null ) {
		$s = $this->dbh->prepare( $sql );
		if( $s->execute( $params ) ) return $s->fetch( PDO::FETCH_ASSOC );
		else throw new Exception($this->dbh->errorInfo, $this->dbh->errorCode);
	}
	
	function getAll( $sql, array $params = null) {
		$s = $this->dbh->prepare( $sql );
		if( $s->execute( $params ) ) return $s->fetchAll( PDO::FETCH_ASSOC );
		else throw new Exception($this->dbh->errorInfo, $this->dbh->errorCode);
	}
	
	function executeMultiple($sql, array $paramset) {
		$s = $this->dbh->prepare( $sql );
		if( $s === false ) throw new Exception($this->dbh->errorInfo, $this->dbh->errorCode);
		else {
			$count = 0;
			$this->startTrans();
			foreach( $paramset as $params ) {
				$response = $s->execute( $params );
				if( $response === false ) {
					$this->rollBack();
					throw new Exception($this->dbh->errorInfo, $this->dbh->errorCode);
				}
				$count += $s->rowCount();
			}
			$this->commit();
			return $count;
		}
	}

	/**
	 *
	 * @param String $table
	 * @return int
	 */
	function fetchAll($table, $where) {
		list( $whereSQL, $bind ) = DBUtil::bind( $where );
		$sql = "SELECT * FROM `$table` WHERE $whereSQL";
		// @var PDOStatement
		$statement = $this->dbh->prepare( $sql );
		$statement->execute( $bind );
		return $statement->fetchAll( PDO::FETCH_ASSOC );
	}

	/**
	 *
	 * @param String $table
	 * @return int
	 */
	function fetchRow($table, $where) {
		list( $whereSQL, $bind ) = DBUtil::bind( $where );
		$sql = "SELECT * FROM `$table` WHERE $whereSQL LIMIT 1";
		// @var PDOStatement
		$statement = $this->dbh->prepare( $sql );
		$statement->execute( $bind );
		return $statement->fetch( PDO::FETCH_ASSOC );
	}

	/**
	 *
	 * @param String $table
	 * @return int
	 */
	function count($table) {
		$sql = "SELECT COUNT(*) FROM `$table`";
		$statement = $this->dbh->prepare( $sql );
		$statement->execute();
		return (int) $statement->fetchColumn();
	}

	/**
	 *
	 * @param string $table the table name
	 * @param array $hashmap associative array of the columns => values to insert
	 * @return int number of affected rows
	 */
	function insert($table, $hashmap) {
		list($fields,$markers,$bind) = DBUtil::bindInsert($hashmap);
		$sql = "INSERT INTO `$table` ($fields) VALUES ($markers)";

		// @var PDOStatement
		$statement = $this->dbh->prepare( $sql );
		return $statement->execute( $bind );
	}

	/**
	 *
	 * @param string $table the table name
	 * @param array $set associative array of the values to set
	 * @param array $where associative array of the where criteria
	 * @return int number of affected rows
	 */
	function update($table, $set, $where) {
		list($setSQL,$whereSQL,$bind) = DBUtil::bindUpdate( $set, $where );
		if( $whereSQL == '' ) $whereSQL = 1;
		$sql = "UPDATE `$table` SET $setSQL WHERE $whereSQL";
		
		// @var PDOStatement
		$statement = $this->dbh->prepare( $sql );
		
		return $statement->execute( $bind );
	}

	/**
	 *
	 * @param string $table the table name
	 * @param array $where associative array of the where criteria (all must be met)
	 * @return int number of affected rows
	 */
	function delete($table, $where) {
		list( $whereSQL, $bind ) = DBUtil::bind( $where );
		if( $whereSQL == '' ) $whereSQL = 1;
		$sql = "DELETE FROM `$table` WHERE $whereSQL";
		$statement = $this->dbh->prepare( $sql );
		return $statement->execute( $bind );
	}

	function lastInsertId() {
		return $this->dbh->lastInsertId();
	}
	
	function insert_ID() {
		return $this->dbh->lastInsertId();
	}
	
	function startTrans() {
		$this->dbh->beginTransaction();
	}
	
	function rollBack() {
		$this->dbh->rollBack();
	}
	
	function commit() {
		$this->dbh->commit();
	}
	
}
